/*
 * Copyright (c) 2017-2021, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description:
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2021/04/02
 *
 * History:
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>

#include "osal_types.h"
#include "osal_mutex.h"
#include "osal_common.h"
#include "lindesplayer.h"
#include "ffcodec.h"
#include "lindcomponent.h"
#include "adev_wrapper.h"

#include "lindtunnel.h"
#include "ldt_error.h"
#include "ldt_ioctrl_def.h"

#define LOG_TAG "sample_ldt_cli"
#include "osal_log.h"

#define LISENCE_KEY "TEST"

#define MAX_SESSION         4
#define WAIT_RESP_TIMEOUT   5000    //5s

#define USE_ES_PLAYER       1
#define ENABLE_PMEM         1
#define ENABLE_VIDEO_FPS    1

#define SAVE_AUDIO          0
#if SAVE_AUDIO
FILE * g_fd = 0;
char * g_filepath = "/mnt/SDCARD/1.audio";
#endif

#define SAVE_VIDEO          0
#if SAVE_VIDEO
FILE * g_fd_v = 0;
char * g_filepath_v = "/mnt/SDCARD/1.video";
#endif

typedef struct _sample_ldt_cli_ctx_t
{
    char                    uid_list[MAX_SESSION][32];

    lock_mutex              sid_mutex;
    int                     sid;
    int                     gid;

    int                     io_running;
    pthread_t               io_tid;

    int                     v_running;
    pthread_t               v_tid;
    av_enc_type_t           venc_type;

    int                     a_running;
    pthread_t               a_tid;
    pthread_t               adec_tid;
    _handle_t               h_adec;
    av_enc_type_t           aenc_type;

    _handle_t               h_adev_out;
    adev_config_t           adev_out_cfg;
    int                     ref_ao;
    lock_mutex              ref_lock;

#if USE_ES_PLAYER
    es_player_handle_t      h_esplayer;
#endif

} sample_ldt_cli_ctx_t;

static int sample_ldt_cli_start_ioctrl(sample_ldt_cli_ctx_t * p_ctx);
static void sample_ldt_cli_stop_ioctrl(sample_ldt_cli_ctx_t * p_ctx);

static int32_t sample_ldt_cli_on_event(void * user, LDT_MSG_e msg, void * ext1, void * ext2)
{
    sample_ldt_cli_ctx_t * p_ctx = user;

    switch (msg)
    {
        case LDT_MSG_LOGIN:
        {
            logd("client login");
            break;
        }
        case LDT_MSG_LOGOUT:
        {
            logd("client logout");
            break;
        }
        case LDT_MSG_SESSION_DISCONN:
        {
            int sid = *(int*)ext1;
            logd("device disconnect, sid: %d", sid);
            do_lock(&p_ctx->sid_mutex);
            p_ctx->v_running = 0;
            if (p_ctx->v_tid != 0)
            {
                pthread_join(p_ctx->v_tid, 0);
                p_ctx->v_tid = 0;
            }
            p_ctx->a_running = 0;
            if (p_ctx->a_tid != 0)
            {
                pthread_join(p_ctx->a_tid, 0);
                p_ctx->a_tid = 0;
            }
            p_ctx->io_running = 0;
            if (p_ctx->io_tid != 0)
            {
                pthread_join(p_ctx->io_tid, 0);
                p_ctx->io_tid = 0;
            }
            LDT_Cli_DisconnDevice(sid);
            p_ctx->sid = -1;
            do_unlock(&p_ctx->sid_mutex);
            break;
        }
        case LDT_MSG_CHANNEL_GROUP_CREATED:
        {
            logi("new channel group sid: %d, gid: %d", *(int*)ext1, *(int*)ext2);
            p_ctx->gid = *(int*)ext2;
            sample_ldt_cli_start_ioctrl(p_ctx);
            break;
        }
        case LDT_MSG_CHANNEL_GROUP_DESTORY:
        {
            logi("channel group destory sid: %d, gid: %d", *(int*)ext1, *(int*)ext2);
            sample_ldt_cli_stop_ioctrl(p_ctx);
            break;
        }
        case LDT_MSG_SESSION_BITRATE:
        {
            int sid = *(int*)ext1;
            int bps = *(int*)ext2;
            logd("sid: %d, %d(kbps)", sid, bps >> 10);
            break;
        }
        default:
            break;
    }

    return 0;
}

static int sample_ldt_handler_channel_ioctrl(sample_ldt_cli_ctx_t * p_ctx, int32_t sid, int32_t gid,
                                    int32_t cmd, uint8_t * data)
{
    switch(cmd)
    {
        case LDT_IOCTRL_IPC_MUSIC_EVENT:
        {
            ldt_ioctrl_music_event_t * p_event = (ldt_ioctrl_music_event_t *)data;
            logd("command: 0x%x, %d/%d", p_event->command, p_event->position, p_event->duration);
            return 0;
        }
        case LDT_IOCTRL_DEVICE_EVENT:
        {
            ldt_ioctrl_event_t * p_event = (ldt_ioctrl_event_t *)data;
            if (p_event->type >= LDT_EVENT_SOUND_BASE
                && p_event->type <= LDT_EVENT_SOUND_END) {
                logd("SOUND_EVENT type: 0x%x", p_event->type);
            } else if (p_event->type >= LDT_EVENT_DHT_BASE
                && p_event->type <= LDT_EVENT_DHT_END) {
                ldt_ioctrl_dht_event_t * p_event = (ldt_ioctrl_dht_event_t *)data;
                logd("DHT_EVENT type: 0x%x, temp: %.2f(C), humi: %.2f(%%)",
                    p_event->type, p_event->temperature, p_event->humidity);
            } else {
                logd("SOUND_EVENT type: 0x%x", p_event->type);
            }
            return 0;
        }
        default:
            logw("unknown cmd: 0x%x", cmd);
            break;
    }

    return 0;
}

static void * thread_ldt_cli_ioctrl_recv(void *param)
{
    sample_ldt_cli_ctx_t * p_ctx = (sample_ldt_cli_ctx_t *)param;
    int ret = 0;
    int sid = p_ctx->sid;
    int gid = p_ctx->gid;
    int cmd = 0;
    uint8_t data[LDT_PKG_MAX_SIZE];
    int size = 0;

    logd("thread_ldt_cli_ioctrl_recv running ...");

    while(p_ctx->io_running)
    {
        size = LDT_PKG_MAX_SIZE;
        ret = LDT_Cli_RecvIOCtrl(sid, gid, &cmd, (uint8_t *)&data, &size, 1000);
        if (ret < 0)
        {
            if (LDTER_TIMEOUT == ret
                || LDTER_AGAIN == ret) {
                logv("io recv timeout");
            } else if (LDTER_SIZE_NOT_ENOUGH == ret) {
                logw("io max_size(%d<%d) is too small", LDT_PKG_MAX_SIZE, size);
            } else {
                logw("io recv failed: %d", ret);
            }
            continue;
        }

        sample_ldt_handler_channel_ioctrl(p_ctx, sid, gid, cmd, (uint8_t *)&data);
    }

_exit_:
    LDT_Cli_FlushRecvIOCtrl(sid, gid);

    logd("thread_ldt_cli_ioctrl_recv exit");
    return 0;
}

static int sample_ldt_cli_start_ioctrl(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;

    // create ioctrl recv thread
    p_ctx->io_running = 1;
    ret = pthread_create(&p_ctx->io_tid, NULL, thread_ldt_cli_ioctrl_recv, (void *)p_ctx);
    if (ret) {
        loge("create thread_ldt_cli_ioctrl_recv failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    return LDTER_OK;
}

static void sample_ldt_cli_stop_ioctrl(sample_ldt_cli_ctx_t * p_ctx)
{
    p_ctx->io_running = 0;
    if (p_ctx->io_tid != 0)
    {
        pthread_join(p_ctx->io_tid, 0);
        p_ctx->io_tid = 0;
    }
}

static void * thread_ldt_cli_video_recv(void *param)
{
    sample_ldt_cli_ctx_t * p_ctx = (sample_ldt_cli_ctx_t *)param;
    int ret = 0;
    int sid = p_ctx->sid;
    int gid = p_ctx->gid;
    LDT_VFrame_t format;
    int size = 0;

    logd("thread_ldt_cli_video_recv_recv running ...");

#if SAVE_VIDEO
    g_fd_v = fopen(g_filepath_v, "wb+");
    if (NULL == g_fd_v)
    {
        loge("Open %s file error", g_filepath_v);
        return 0;
    }
#endif

#if USE_ES_PLAYER
#if ENABLE_PMEM
    ret = esplayer_init_reserved_mem(16*3, 480*272);  // (1920*1088*3/2)*2
    if (ret < 0) {
        goto _exit_;
    }
#endif

    p_ctx->h_esplayer = esplayer_create();
    if (!p_ctx->h_esplayer)
    {
        goto _exit_;
    }
    lindesplayer_config_t config = {0};
    config.user = NULL;
    config.cb = NULL;
    config.disp_pos.left = 0;
    config.disp_pos.top = 0;
    config.disp_pos.width = 0;
    config.disp_pos.height = 0;
    config.rotate = ROTATE_DEGREE_90;
    config.venc_type = p_ctx->venc_type;
    esplayer_set_parameters(p_ctx->h_esplayer, &config);
    ret = esplayer_prepare(p_ctx->h_esplayer);
    if (ret < 0) {
        goto _exit_;
    }
#endif

#if ENABLE_VIDEO_FPS
    int vfps = 0;
    int64_t last_tm_ms = get_tick_ms();
#endif

    int max_size = 256*1024;
    uint8_t * p_frame = (uint8_t *)malloc(max_size);

    while(p_ctx->v_running)
    {
        size = max_size;
        ret = LDT_Cli_RecvVideo(sid, gid, &format, p_frame, &size, 1000);
        if (ret < 0)
        {
            if (LDTER_TIMEOUT == ret) {
                logw("v recv timeout");
            } else if (LDTER_SIZE_NOT_ENOUGH == ret) {
                logw("v max_size(%d<%d) is too small", max_size, size);
            } else {
                logw("v recv failed: %d", ret);
            }
            continue;
        }

#if SAVE_VIDEO
        if (g_fd_v)
        {
            fwrite(p_frame, size, 1, g_fd_v);
        }
#endif

        logv("recv video frame codec: %d, format: %d, pts: %u, size: %d",
            format.codec_id, format.format, format.timestamp, size);
#if USE_ES_PLAYER
        esplayer_send_es(p_ctx->h_esplayer, p_frame, size);
#endif
#if ENABLE_VIDEO_FPS
    if (get_tick_ms() - last_tm_ms >= 1000) {
        logi("video fps: %d", vfps);
        last_tm_ms = get_tick_ms();
        vfps = 0;
    }
    vfps++;
#endif
    }

    free(p_frame);

_exit_:
#if USE_ES_PLAYER
    if (p_ctx->h_esplayer) {
        esplayer_destroy(p_ctx->h_esplayer);
    }
#if ENABLE_PMEM
    esplayer_deinit_reserved_mem();
#endif
#endif

#if SAVE_VIDEO
    if (NULL != g_fd_v)
    {
        fclose(g_fd_v);
        g_fd_v = NULL;
    }
#endif

    LDT_Cli_FlushRecvVideo(sid, gid);

    logd("thread_ldt_cli_video_recv_recv exit");

    return 0;
}

static int sample_ldt_cli_start_video(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;

    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    {
        ldt_ioctrl_get_vfmt_req_t req = {0};
        req.camera_id = 0;
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_IPC_GET_VIDEO_FMT_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_IPC_GET_VIDEO_FMT_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_get_vfmt_resp_t resq = {0};
        int cmd = LDT_IOCTRL_IPC_GET_VIDEO_FMT_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_IPC_GET_VIDEO_FMT_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }
        logi("video codec: %d, quality: %d, [%dx%d]", resq.codec, resq.quality, resq.width, resq.height);
        switch (resq.codec)
        {
            case LDT_CODEC_VIDEO_H264:
                p_ctx->venc_type = AV_ENC_TYPE_H264;
                break;
            case LDT_CODEC_VIDEO_H265:
                p_ctx->venc_type = AV_ENC_TYPE_H265;
                break;
            default:
                logw("unknown video encoder type, default use h264");
                p_ctx->venc_type = AV_ENC_TYPE_H264;
                break;
        }
    }

    ldt_ioctrl_video_start_req_t req = {0};
    req.camera_id = 0;
    ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_IPC_VIDEO_START_REQ,
                            (int8_t *)&req, sizeof(req));
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_VIDEO_START_REQ failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    ldt_ioctrl_video_start_resp_t resq = {0};
    int cmd = LDT_IOCTRL_IPC_VIDEO_START_RESP;
    int size = sizeof(resq);
    ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                            (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_VIDEO_START_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    p_ctx->v_running = 1;
    ret = pthread_create(&p_ctx->v_tid, NULL, thread_ldt_cli_video_recv, (void *)p_ctx);
    if (ret) {
        loge("create thread_ldt_cli_video_recv failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    return LDTER_OK;
}

static int sample_ldt_cli_stop_video(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;

    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    ldt_ioctrl_video_stop_req_t req = {0};
    req.camera_id = 0;
    ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_IPC_VIDEO_STOP_REQ,
                            (int8_t *)&req, sizeof(req));
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_VIDEO_STOP_REQ failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    ldt_ioctrl_video_stop_resp_t resq = {0};
    int cmd = LDT_IOCTRL_IPC_VIDEO_STOP_RESP;
    int size = sizeof(resq);
    ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                            (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_VIDEO_STOP_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    p_ctx->v_running = 0;
    if (p_ctx->v_tid != 0)
    {
        pthread_join(p_ctx->v_tid, 0);
        p_ctx->v_tid = 0;
    }

    return LDTER_OK;
}

static int sample_ldt_cli_start_ao(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;

    if (p_ctx == NULL)
    {
        loge("error input parameters");
        return -1;
    }

    do_lock(&p_ctx->ref_lock);
    if (p_ctx->ref_ao == 0)
    {
        p_ctx->h_adev_out = adev_out_create();
        if (!p_ctx->h_adev_out) {
            goto _exit_;
        }
        memset(&p_ctx->adev_out_cfg, 0, sizeof(p_ctx->adev_out_cfg));
        p_ctx->adev_out_cfg.device = AUDIO_DEVICE_CODEC;
        p_ctx->adev_out_cfg.rate = LDT_AUDIO_SAMPLE_16K;
        p_ctx->adev_out_cfg.channels = LDT_AUDIO_CHANNEL_MONO;
        p_ctx->adev_out_cfg.bits = LDT_AUDIO_BITS_16;
        p_ctx->adev_out_cfg.period_size = 512;
        p_ctx->adev_out_cfg.period_count = 2;
        ret = adev_out_open(p_ctx->h_adev_out, &p_ctx->adev_out_cfg);
        if (ret < 0) {
            loge("adev_out_open failed");
        } else {
            logi("adev_out_open ok");
        }
    }
    p_ctx->ref_ao++;

_exit_:
    do_unlock(&p_ctx->ref_lock);

    return ret;
}

static int sample_ldt_cli_stop_ao(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;

    do_lock(&p_ctx->ref_lock);
    p_ctx->ref_ao--;
    if (p_ctx->ref_ao == 0)
    {
        ret = adev_out_close(p_ctx->h_adev_out);
        if (ret < 0)
        {
            loge("adev_out_close failed");
        }
        logi("adev_out_close ok");

        adev_out_destroy(p_ctx->h_adev_out);
        p_ctx->h_adev_out = 0;
    }
    if (p_ctx->ref_ao < 0)
    {
        logw("audio out device has been closed, should not closed again");
        p_ctx->ref_ao = 0;
    }
    do_unlock(&p_ctx->ref_lock);

    return ret;
}

static void * thread_ldt_cli_audio_dec(void * param)
{
    sample_ldt_cli_ctx_t * p_ctx = (sample_ldt_cli_ctx_t *)param;
    int sid = p_ctx->sid;
    int gid = p_ctx->gid;
    int ret = 0;
    int cmd = 0;
    LDT_AFrame_t format = {0};  // no not use
    uint8_t data[LDT_PKG_MAX_SIZE];
    int size = 0;

    logd("_ldt_ctrl_thread_speaker_adec sid: %d, running ...", sid);

    while(p_ctx->a_running)
    {
        av_frame2_t * p_frame = NULL;
        ret = ff_adec_receive_frame2(p_ctx->h_adec, &p_frame);
        if (ret == 0) {
            //printf("got dec audio data pts: %lld, 0x%08x, size: %d\n", p_frame->pts, p_frame->data[0], p_frame->size[0]);

            adev_out_write(p_ctx->h_adev_out, p_frame->data[0], p_frame->size[0]);

            ff_adec_return_frame2(p_ctx->h_adec, p_frame);
        }
    }

    logd("_ldt_ctrl_thread_speaker_adec sid: %d, exit", sid);

    return 0;
}

static void * thread_ldt_cli_audio_recv(void *param)
{
    sample_ldt_cli_ctx_t * p_ctx = (sample_ldt_cli_ctx_t *)param;
    int ret = 0;
    int sid = p_ctx->sid;
    int gid = p_ctx->gid;
    LDT_AFrame_t format;
    int size = 0;

    logd("thread_ldt_cli_audio_recv running ...");

    uint8_t data[LDT_PKG_MAX_SIZE];

#if SAVE_AUDIO
    g_fd = fopen(g_filepath, "wb+");
    if (NULL == g_fd)
    {
        loge("Open %s file error", g_filepath);
        return 0;
    }
#endif

    if (p_ctx->aenc_type != AV_ENC_TYPE_PCM) {
        // prepare audio decode if needed
        p_ctx->h_adec = ff_adec_create();
        if (!p_ctx->h_adec) {
            loge("ff_adec_create failed");
            goto _exit1_;
        }

        ffadec_config_t adec_cfg = {0};
        adec_cfg.aenc_type      = p_ctx->aenc_type;
        adec_cfg.channels       = LDT_AUDIO_CHANNEL_MONO;
        adec_cfg.sample_rate    = LDT_AUDIO_SAMPLE_16K;
        adec_cfg.bits           = LDT_AUDIO_BITS_16;
        adec_cfg.p_priv         = NULL;
        ret = ff_adec_start(p_ctx->h_adec, &adec_cfg);
        if (ret < 0) {
            loge("ff_adec_start failed");
            goto _exit2_;
        }

        ret = pthread_create(&p_ctx->adec_tid, NULL, thread_ldt_cli_audio_dec, (void *)p_ctx);
        if (ret) {
            loge("create thread_ldt_cli_audio_dec failed: %s", strerror(errno));
            goto _exit3_;
        }
    }

    ret = sample_ldt_cli_start_ao(p_ctx);
    if (ret < 0) {
        goto _exit3_;
    }

    while(p_ctx->a_running)
    {
        size = LDT_PKG_MAX_SIZE;
        ret = LDT_Cli_RecvAudio(sid, gid, &format, data, &size, 1000);
        if (ret < 0)
        {
            if (LDTER_TIMEOUT == ret) {
                logw("a recv timeout");
            } else if (LDTER_SIZE_NOT_ENOUGH == ret) {
                logw("a max_size(%d<%d) is too small", LDT_PKG_MAX_SIZE, size);
            } else {
                logw("a recv failed: %d", ret);
            }
            continue;
        }

        logv("recv audio frame size: %d", size);
        if (p_ctx->aenc_type == AV_ENC_TYPE_PCM)
        {
            //
            adev_out_write(p_ctx->h_adev_out, data, size);
        }
        else
        {
#if SAVE_AUDIO
            if (g_fd)
            {
                fwrite(data, size, 1, g_fd);
            }
#endif
            av_frame2_t aframe = {0};
            aframe.data[0] = data;
            aframe.size[0] = size;
            ff_adec_send_data2(p_ctx->h_adec, &aframe);
        }
    }

    LDT_Cli_FlushRecvAudio(sid, gid);

    sample_ldt_cli_stop_ao(p_ctx);

_exit3_:
    ff_adec_stop(p_ctx->h_adec);

_exit2_:
    ff_adec_destroy(p_ctx->h_adec);
    p_ctx->h_adec = 0;

_exit1_:

    logd("thread_ldt_cli_audio_recv exit");
#if SAVE_AUDIO
    if (NULL != g_fd)
    {
        fclose(g_fd);
        g_fd = NULL;
    }
#endif

    if (p_ctx->adec_tid != 0)
    {
        pthread_join(p_ctx->adec_tid, 0);
        p_ctx->adec_tid = 0;
    }

    return 0;
}

static int sample_ldt_cli_start_audio(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;

    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    ldt_ioctrl_audio_start_req_t req = {0};
    ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_IPC_AUDIO_START_REQ,
                            (int8_t *)&req, sizeof(req));
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_AUDIO_START_REQ failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    ldt_ioctrl_audio_start_resp_t resq = {0};
    int cmd = LDT_IOCTRL_IPC_AUDIO_START_RESP;
    int size = sizeof(resq);
    ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                            (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_AUDIO_START_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    p_ctx->a_running = 1;
    ret = pthread_create(&p_ctx->a_tid, NULL, thread_ldt_cli_audio_recv, (void *)p_ctx);
    if (ret) {
        loge("create thread_ldt_cli_audio_recv failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    return LDTER_OK;
}

static int sample_ldt_cli_stop_audio(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;

    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    ldt_ioctrl_audio_stop_req_t req = {0};
    ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_IPC_AUDIO_STOP_REQ,
                            (int8_t *)&req, sizeof(req));
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_AUDIO_STOP_REQ failed: %s", strerror(errno));
        goto _exit_;
    }

    ldt_ioctrl_audio_stop_resp_t resq = {0};
    int cmd = LDT_IOCTRL_IPC_AUDIO_STOP_RESP;
    int size = sizeof(resq);
    ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                            (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_AUDIO_STOP_RESP failed, ret: %d", ret);
        goto _exit_;
    }

_exit_:
    p_ctx->a_running = 0;
    if (p_ctx->a_tid != 0)
    {
        pthread_join(p_ctx->a_tid, 0);
        p_ctx->a_tid = 0;
    }

    return LDTER_OK;
}

static int sample_ldt_cli_music(sample_ldt_cli_ctx_t * p_ctx, LDT_MUSIC_CTRL_e command)
{
    int ret = 0;

    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    ldt_ioctrl_music_req_t req = {0};
    req.command = command;
    if (command == LDT_MUSIC_CTRL_START)
    {
        req.id = 1;
        // strcpy(req.url, "http://aod.cos.tx.xmcdn.com/group3/M03/AE/29/wKgDslLCmAaSzKXZAFbLuvsW4ds663.mp3");
    }
    ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_IPC_MUSIC_CONTROL_REQ,
                            (int8_t *)&req, sizeof(req));
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_MUSIC_CONTROL_REQ(0x%x) failed: %s", command, strerror(errno));
        return LDTER_FAILED;
    }

    ldt_ioctrl_music_resp_t resq = {0};
    int cmd = LDT_IOCTRL_IPC_MUSIC_CONTROL_RESP;
    int size = sizeof(resq);
    ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                            (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_MUSIC_CONTROL_RESP(0x%x) failed, ret: %d", command, ret);
        return LDTER_FAILED;
    }

    if (command == LDT_MUSIC_CTRL_START) {
        logi("music duration: %d(s)", resq.duration);
    }

    return LDTER_OK;
}

static int sample_ldt_cli_reset_motor(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;
    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    ldt_ioctrl_set_ptz_req_t req = {0};
    req.control = LDT_PTZ_RESET_CENTER;
    ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_SET_PTZ_REQ,
                            (int8_t *)&req, sizeof(req));
    if (ret < 0) {
        loge("LDT_IOCTRL_SET_PTZ_REQ failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    ldt_ioctrl_set_ptz_resp_t resq = {0};
    int cmd = LDT_IOCTRL_SET_PTZ_RESP;
    int size = sizeof(resq);
    ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                            (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
    if (ret < 0) {
        loge("LDT_IOCTRL_SET_PTZ_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }
    return LDTER_OK;
}

static int sample_ldt_cli_get_xxx(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;
    int irmode = 0;
    int alarm_mode = 0;

    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    /*
     * set time
     */
    {
        ldt_ioctrl_set_time_req_t req = {0};
        req.utc = time(NULL);
        req.tz = 0;
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_SYS_SET_TIME_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_SYS_SET_TIME_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_set_time_resp_t resq = {0};
        int cmd = LDT_IOCTRL_SYS_SET_TIME_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_SYS_SET_TIME_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }
    }

    /*
     * get ir mode
     */
    {
        ldt_ioctrl_get_ir_mode_req_t req = {0};
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_GET_IR_MODE_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_IR_MODE_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_get_ir_mode_resp_t resq = {0};
        int cmd = LDT_IOCTRL_GET_IR_MODE_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_IR_MODE_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }

        logi("ir mode: %d", resq.mode);
        irmode = resq.mode;
    }

    /*
     * set ir mode
     */
    {
        ldt_ioctrl_set_ir_mode_req_t req = {0};
        req.mode = irmode ? 0 : 1;
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_SET_IR_MODE_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_SET_IR_MODE_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_set_ir_mode_resp_t resq = {0};
        int cmd = LDT_IOCTRL_SET_IR_MODE_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_SET_IR_MODE_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }
    }

    /*
     * get dht
     */
    {
        ldt_ioctrl_get_dht_cur_req_t req = {0};
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_GET_DHT_SENSOR_CURRENT_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_DHT_SENSOR_CURRENT_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_get_dht_cur_resp_t resq = {0};
        int cmd = LDT_IOCTRL_GET_DHT_SENSOR_CURRENT_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_DHT_SENSOR_CURRENT_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }

        logi("ir mode: %.2f(C), %.2f(%%)", resq.temperature, resq.humidity);
    }

    /*
     * get alarm temperature
     */
    {
        ldt_ioctrl_get_alarm_temp_req_t req = {0};
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_GET_ALARM_TEMPERATURE_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_ALARM_TEMPERATURE_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_get_alarm_temp_resp_t resq = {0};
        int cmd = LDT_IOCTRL_GET_ALARM_TEMPERATURE_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_ALARM_TEMPERATURE_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }

        logd("get alarm temperature mode: %d [%d, %d], unit: %d",
            resq.mode,
            resq.temperature_min, resq.temperature_max,
            resq.unit);

        alarm_mode = resq.mode;
    }

    /*
     * set alarm temperature
     */
    {
        ldt_ioctrl_set_alarm_temp_req_t req = {0};
        req.mode = alarm_mode ? 0 : 1;
        req.temperature_min = 20;
        req.temperature_max = 25;
        req.unit = 0;
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_SET_ALARM_TEMPERATURE_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_SET_ALARM_TEMPERATURE_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_set_alarm_temp_resp_t resq = {0};
        int cmd = LDT_IOCTRL_SET_ALARM_TEMPERATURE_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_SET_ALARM_TEMPERATURE_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }
    }

    /*
     * get alarm humidity
     */
    {
        ldt_ioctrl_get_alarm_humi_req_t req = {0};
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_GET_ALARM_HUMIDITY_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_ALARM_HUMIDITY_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_get_alarm_humi_resp_t resq = {0};
        int cmd = LDT_IOCTRL_GET_ALARM_HUMIDITY_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_ALARM_HUMIDITY_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }

        logd("get alarm humidity mode: %d [%d, %d]",
            resq.mode, resq.humidity_min, resq.humidity_max);

        alarm_mode = resq.mode;
    }

    /*
     * set alarm humidity
     */
    {
        ldt_ioctrl_set_alarm_humi_req_t req = {0};
        req.mode = alarm_mode ? 0 : 1;
        req.humidity_min = 30;
        req.humidity_max = 50;
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_SET_ALARM_HUMIDITY_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_SET_ALARM_HUMIDITY_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_set_alarm_humi_resp_t resq = {0};
        int cmd = LDT_IOCTRL_SET_ALARM_HUMIDITY_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_SET_ALARM_HUMIDITY_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }
    }

    /*
     * get alarm sound
     */
    {
        ldt_ioctrl_get_alarm_sound_req_t req = {0};
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_GET_ALARM_SOUND_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_ALARM_SOUND_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_get_alarm_sound_resp_t resq = {0};
        int cmd = LDT_IOCTRL_GET_ALARM_SOUND_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_GET_ALARM_SOUND_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }

        logd("get alarm sound mode: %d, sensitivity: %d",
            resq.mode, resq.sensitivity);

        alarm_mode = resq.mode;
    }

    /*
     * set alarm sound
     */
    {
        ldt_ioctrl_set_alarm_sound_req_t req = {0};
        req.mode = alarm_mode ? 0 : 1;
        req.sensitivity = 90;
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_SET_ALARM_SOUND_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("LDT_IOCTRL_SET_ALARM_SOUND_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        ldt_ioctrl_set_alarm_humi_resp_t resq = {0};
        int cmd = LDT_IOCTRL_SET_ALARM_SOUND_RESP;
        int size = sizeof(resq);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resq, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("LDT_IOCTRL_SET_ALARM_SOUND_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }
    }

    /*
     * set zoom
     */
    {
        int i = 0;
        for (i = 0; i < 5; i++)
        {
            ldt_ioctrl_set_ptz_req_t req = {0};
            req.control = LDT_PTZ_ZOOM_IN;
            req.zoom_step = 10;
            ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_SET_PTZ_REQ,
                                    (int8_t *)&req, sizeof(req));
            if (ret < 0) {
                loge("LDT_IOCTRL_SET_PTZ_REQ failed: %s", strerror(errno));
                return LDTER_FAILED;
            }

            sleep(1);
        }
        for (i = 0; i < 5; i++)
        {
            ldt_ioctrl_set_ptz_req_t req = {0};
            req.control = LDT_PTZ_ZOOM_OUT;
            req.zoom_step = 10;
            ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, LDT_IOCTRL_SET_PTZ_REQ,
                                    (int8_t *)&req, sizeof(req));
            if (ret < 0) {
                loge("LDT_IOCTRL_SET_PTZ_REQ failed: %s", strerror(errno));
                return LDTER_FAILED;
            }

            sleep(1);
        }
    }

    return 0;
}

static int sample_ldt_cli_process(const char * uid, const char * password)
{
    int ret = 0;
    sample_ldt_cli_ctx_t contex = {0};
    sample_ldt_cli_ctx_t * p_ctx = &contex;
    p_ctx->sid = -1;
    p_ctx->gid = -1;

    p_ctx->aenc_type = AV_ENC_TYPE_MP3;

    init_lock(&p_ctx->sid_mutex);
    init_lock(&p_ctx->ref_lock);

    LDT_Cfg_t config = {0};
    config.on_event = sample_ldt_cli_on_event;
    config.user = p_ctx;
    config.max_session = MAX_SESSION;
    config.timeout_ms = 5000;
    ret = LDT_Cli_Initialize(&config, LISENCE_KEY);
    if (ret < 0)
    {
        loge("LDT_Cli_Initialize failed, ret: %d", ret);
        goto _exit1_;
    }

    while(1)
    {
        signed char key_val;
        ret = wait_input_char(&key_val, 100000);
        if (ret < 0)
        {
            break;
        }
        else if (ret == 0)
        {
            continue;
        }

        if (key_val > 0 && key_val)
        {
            if (key_val != '\n')
            {
                printf("********* key_val: %c ********** \n", key_val);
            }

            if (key_val == 'q' || key_val == 'Q')
            {
                break;
            }
            else if (key_val == 'f' || key_val == 'F')
            {
                int n = LDT_Cli_QueryDevice((char **)&p_ctx->uid_list, MAX_SESSION);
                if (n < 0)
                {
                    logw("LDT_Cli_QueryDevice failed");
                    continue;
                }
                logd("find device count: %d", n);
                for (; n > 0; n--)
                {
                    logd("\t uid: %s", p_ctx->uid_list[n - 1]);
                }
                continue;
            }
            else if (key_val == 'c')
            {
                logd("conn device: %s, psw: %s", uid, password);
                int sid = LDT_Cli_ConnDevice(uid, password, 5000);
                if (sid < 0)
                {
                    logw("LDT_Cli_ConnDevice(%s) failed", uid);
                    continue;
                }
                p_ctx->sid = sid;
                logd("LDT_Cli_ConnDevice success, sid: %d", sid);
                continue;
            }
            else if (key_val == 'C')
            {
                logd("LDT_Cli_DisconnDevice success, sid: %d", p_ctx->sid);
                LDT_Cli_DisconnDevice(p_ctx->sid);
                p_ctx->sid = -1;
                continue;
            }
            else if (key_val == 'g')
            {
                do_lock(&p_ctx->sid_mutex);
                LDT_ChnGrpCfg_t grp_cfg = {0};
                int gid = LDT_Cli_CreateChnGrp(p_ctx->sid, &grp_cfg);
                if (gid < 0)
                {
                    logw("LDT_Cli_CreateChnGrp failed");
                    do_unlock(&p_ctx->sid_mutex);
                    continue;
                }
                p_ctx->gid = gid;
                do_unlock(&p_ctx->sid_mutex);

                logd("LDT_Cli_CreateChnGrp success, gid: %d", gid);
                continue;
            }
            else if (key_val == 'G')
            {
                do_lock(&p_ctx->sid_mutex);
                if (p_ctx->sid >= 0 && p_ctx->gid >= 0)
                {
                    LDT_Cli_DestroyChnGrp(p_ctx->sid, p_ctx->gid);
                    logd("LDT_Cli_DestroyChnGrp, sid: %d, gid: %d", p_ctx->sid, p_ctx->gid);
                }
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'v')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_start_video(p_ctx);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'V')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_stop_video(p_ctx);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'a')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_start_audio(p_ctx);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'A')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_stop_audio(p_ctx);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'm')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_music(p_ctx, LDT_MUSIC_CTRL_START);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'M')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_music(p_ctx, LDT_MUSIC_CTRL_STOP);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'p')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_music(p_ctx, LDT_MUSIC_CTRL_PAUSE);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'P')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_music(p_ctx, LDT_MUSIC_CTRL_RESUME);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'x')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_get_xxx(p_ctx);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            else if (key_val == 'r')
            {
                do_lock(&p_ctx->sid_mutex);
                sample_ldt_cli_reset_motor(p_ctx);
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
        }
    }

    if (p_ctx->v_running)
    {
        sample_ldt_cli_stop_video(p_ctx);
    }
    if (p_ctx->a_running)
    {
        sample_ldt_cli_stop_audio(p_ctx);
    }
    if (p_ctx->io_running)
    {
        sample_ldt_cli_stop_ioctrl(p_ctx);
    }
    if (p_ctx->sid >= 0)
    {
        LDT_Cli_DisconnDevice(p_ctx->sid);
        logd("LDT_Cli_DisconnDevice ok, sid: %d", p_ctx->sid);
    }

_exit2_:
    LDT_Cli_Cleanup();

_exit1_:
    destroy_lock(&p_ctx->sid_mutex);
    destroy_lock(&p_ctx->ref_lock);

    return 0;
}

static void _sample_ldt_cli_usage(char * this)
{
    printf("\033[32m");
    printf("Usage: \n");
    printf("    %s -u [uid] -p [password] \n", this);
    printf("        -u [uid]        : the uid of ldt_device, defaut: '0001' \n");
    printf("        -p [password]   : the password of ldt_device, defaut: '666666' \n");
    printf("    such as: %s -u 0001 -p 666666 \n\n", this);
    printf("After %s is running, there are some useful command: \n", this);
    printf("        'q' or 'Q': exit this app \n");
    printf("        'f' or 'F': find ldt_devices \n");
    printf("        'c'       : connect the ldt_dev by 'uid' \n");
    printf("        'C'       : disconnect the ldt_dev \n");
    printf("        'g'       : create a channel group \n");
    printf("        'G'       : destory the channel group \n");
    printf("        'v'       : request a remote video stream \n");
    printf("        'V'       : stop the remote video stream \n");
    printf("        'a'       : request a remote audio stream \n");
    printf("        'A'       : stop the remote audio stream \n");
    printf("\033[0m\n");
}

int main(int argc, char *argv[])
{
    int ret = 0;
    int i = 0;
    char * uid = "0001";
    char * password = "666666";

    if ((argc == 2)
        && (!strcmp(argv[1], "-h")))
    {
        _sample_ldt_cli_usage(argv[0]);
        return 0;
    }

    for (i = 1; i < argc; )
    {
        if (!strcmp(argv[i], "-u"))
        {
            uid = argv[i+1];
            i += 2;
        }
        else if (!strcmp(argv[i], "-p"))
        {
            password = argv[i+1];
            i += 2;
        }
        else
        {
            i++;
        }
    }

    sample_ldt_cli_process(uid, password);

    logd("bye bye");

    return 0;
}
